package com.stm.bi.etl.format;

import com.stm.base.api.DictionaryRemoteService;
import com.stm.base.dto.vo.DictVO;
import com.stm.bi.core.util.DateUtils;
import com.stm.bi.core.util.ETLUtils;
import com.stm.bi.dto.query.ods.OdsListQry;
import com.stm.bi.enums.DateType;
import com.stm.bi.etl.AbstractDataEtlTask;
import com.stm.bi.repository.dataobject.ods.OdsDeptDO;
import com.stm.bi.repository.dataobject.ods.OdsShopDO;
import com.stm.bi.repository.dataobject.ods.OdsWarehouseDO;
import com.stm.bi.repository.ods.OdsDeptRepository;
import com.stm.bi.repository.ods.OdsShopRepository;
import com.stm.bi.repository.ods.OdsWarehouseRepository;
import com.stm.framework.core.util.FileResourceLoader;
import com.stm.framework.dto.Response;
import com.stm.ucenter.api.DeptRemoteService;
import com.stm.ucenter.dto.query.org.DeptListQry;
import com.stm.ucenter.dto.vo.org.DeptVO;
import com.stm.ucenter.dto.vo.org.ShopVO;
import com.stm.ucenter.enums.DeptType;
import jakarta.annotation.Resource;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public abstract class AbsFormatEtlTask extends AbstractDataEtlTask {

    @Resource
    private OdsDeptRepository odsDeptRepository;
    @Resource
    private OdsShopRepository odsShopRepository;
    @Resource
    private DictionaryRemoteService dictionaryRemoteService;

    @Override
    public boolean isTenantLine() {
        return true;
    }

    @Override
    public boolean syncWithTimeRange() {
        return true;
    }


    @Override
    public int getDelayDays() {
        return 0;
    }


    public abstract String getFormatType();

    public abstract List<String> getFormatField();

    public abstract String getAttachedSql();

    @Override
    public List<DateType> getEtlDateTypes() {
        return Arrays.asList(DateType.YEAR, DateType.MONTH, DateType.WEEK, DateType.DAY);
    }

    @Override
    public List<Map> extract(Long orgId, String orgNo, DateType dateType, LocalDateTime startDateTime, LocalDateTime endDateTime) {

        String sql = FileResourceLoader.getResourceAsString("/sql/format/default.sql");
        String attachedSql = getAttachedSql();
        Response<List<DictVO>> dictResp = dictionaryRemoteService.selectList(getFormatType());
        if(!dictResp.isSuccess()){
            throw new RuntimeException("字典查询失败");
        }
        List<DictVO> formats = dictResp.getData().stream()
                .flatMap(d -> d.getChildren().stream())
                .toList();

        Map<Long, Map<String, List<Long>>> deptShopMap = getDeptShopMap(orgId, orgNo, getFormatType(), formats);

        List<String> salesType = Arrays.asList("dealing", "payment");
        String endDate = DateUtils.getDateStrByDateType(endDateTime, dateType);
        List<String> etlDateList = DateUtils.getETLDateList(startDateTime, endDateTime, dateType);

//        log.info("SQL查询：{}",sql);

        List<Map> data = new ArrayList<>();
        for(String date: etlDateList){
            List<Map> maps = jdbcService.selectList(sql, new Object[]{orgNo, dateType.getValue(), date, date});
            List<Map> attdMaps = jdbcService.selectList(attachedSql, new Object[]{orgNo, dateType.getValue(), date, date});

            for(String type: salesType){
                for(DictVO format: formats){
                    deptShopMap.forEach((deptId, formatMap) -> {
                        List<Long> shopIds = formatMap.get(format.getValue());
                        List<Map> shopMaps = maps.stream()
                                .filter(m -> Objects.equals(m.get("sales_type"), type))
                                .filter(m -> shopIds.contains(m.get("dept_id")))
                                .toList();

                        Map map = attdMaps.stream()
                                .filter(b -> Objects.equals(b.get("dept_id"), deptId))
                                .findAny().orElse(new HashMap());

                        Map row = new HashMap<>();

                        BigDecimal salesAmount = shopMaps.stream()
                                .map(m -> (BigDecimal) m.get("sales_amount"))
                                .reduce(BigDecimal.ZERO, BigDecimal::add);

                        BigDecimal avgDaySalesAmount = shopMaps.stream()
                                .map(m -> (BigDecimal) m.get("avg_per_day_sales_amount"))
                                .reduce(BigDecimal.ZERO, BigDecimal::add);

                        BigDecimal newCustomerSales = shopMaps.stream()
                                .map(m -> (BigDecimal) m.get("new_customer_sales"))
                                .reduce(BigDecimal.ZERO, BigDecimal::add);

                        BigDecimal oldCustomerSales = shopMaps.stream()
                                .map(m -> (BigDecimal) m.get("old_customer_sales"))
                                .reduce(BigDecimal.ZERO, BigDecimal::add);

                        BigDecimal numberOfTransactions = (BigDecimal) map.get("number_of_transactions");
                        BigDecimal avgDayNumberOfTransactions = (BigDecimal) map.get("avg_day_number_of_transactions");
                        BigDecimal newCustomers = (BigDecimal) map.get("new_customers");
                        BigDecimal oldCustomers = (BigDecimal) map.get("old_customers");
                        BigDecimal numberOfContOrder = (BigDecimal) map.get("number_of_cont_order");

                        BigDecimal perCustomerTransaction = BigDecimal.ZERO;
                        if(numberOfTransactions.doubleValue() != 0){
                            perCustomerTransaction = salesAmount.divide(numberOfTransactions, 2, RoundingMode.HALF_UP);
                        }

                        BigDecimal newCustomerTransactions = BigDecimal.ZERO;
                        if(newCustomers.doubleValue() != 0){
                            newCustomerTransactions = newCustomerSales.divide(newCustomers, 2, RoundingMode.HALF_UP);
                        }

                        BigDecimal oldCustomerTransactions = BigDecimal.ZERO;
                        if(oldCustomers.doubleValue() != 0){
                            oldCustomerTransactions = oldCustomerSales.divide(oldCustomers, 2, RoundingMode.HALF_UP);
                        }

                        row.put("org_no", orgNo);
                        row.put("date", date);
                        row.put("date_type", dateType.getValue());
                        row.put("dept_id", deptId);
                        row.put("sales_type", type);
                        row.put(getFormatField().get(0), format.getValue());
                        row.put(getFormatField().get(1), format.getLabel());
                        row.put("sales_amount", salesAmount);
                        row.put("avg_per_day_sales_amount", avgDaySalesAmount);
                        row.put("new_customer_sales", newCustomerSales);
                        row.put("old_customer_sales", oldCustomerSales);
                        row.put("number_of_transactions", numberOfTransactions);
                        row.put("avg_day_number_of_transactions", avgDayNumberOfTransactions);
                        row.put("new_customers", newCustomers);
                        row.put("old_customers", oldCustomers);
                        row.put("number_of_cont_order", numberOfContOrder);
                        row.put("per_customer_transaction", perCustomerTransaction);
                        row.put("new_customer_transactions", newCustomerTransactions);
                        row.put("old_customer_transactions", oldCustomerTransactions);
                        data.add(row);
                    });
                }
            }
        }

        return data;

    }

    private Map<Long, Map<String, List<Long>>> getDeptShopMap(Long orgId, String orgNo, String formatType,List<DictVO> formats) {
        //仓库与部门的对应关系逻辑
        List<OdsDeptDO> depts = odsDeptRepository.selectList(new OdsListQry().setOrgId(orgId));
        if(depts.isEmpty()){
            log.info("部门为空");
            return new HashMap<>();
        }

        List<OdsShopDO> shops = odsShopRepository.selectList(new OdsListQry().setOrgNo(orgNo));

        Map<Long, Map<String, List<Long>>> res = new HashMap<>();
        for(OdsDeptDO d: depts){
            List<Long> childrenId = depts.stream()
                    .filter(dept -> dept.getAncestors().contains(d.getId() + ""))
                    .filter(dept -> dept.getType().equals(DeptType.SHOP.getValue()))
                    .map(OdsDeptDO::getId)
                    .toList();

            Map<String, List<Long>> formatMap = new HashMap<>();
            for(DictVO format: formats){

                Stream<OdsShopDO> shopStream = null;
                if("shop_cooperation_format".equals(formatType)){
                    shopStream = shops.stream().filter(shop -> shop.getCooperateFormat().equals(format.getValue()));
                }else if("shop_life_format".equals(formatType)){
                    shopStream = shops.stream().filter(shop -> shop.getLifeFormat().equals(format.getValue()));
                }else if("shop_invest_format".equals(formatType)){
                    shopStream = shops.stream().filter(shop -> shop.getInvestFormat().equals(format.getValue()));
                }

                List<Long> shopIds = shopStream
                        .filter(shop -> childrenId.contains(shop.getDeptId()))
                        .map(OdsShopDO::getDeptId)
                        .collect(Collectors.toList());

                formatMap.put(format.getValue(), shopIds);
            }
            res.put(d.getId(), formatMap);
        }

        return res;
    }

}
